﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace System.Threading.Actors.Utilities
{
    // We hae multiple ITcpClient implementations - one using the .NET TcpClient type, while others simulate it using
    // WinRT's SocketStream classes.  This class will load other implementations, get a delegate to the right creation method,
    // and let us open network connections in a portable way.
    public static class LightupITcpClientFactory
    {
        // A catalog of candidates for creating ITcpClient objects, if no explicit creation function is registered.
        // Key = assembly as string, value = type as string.
        public static Dictionary<string, string> s_ClientProviderCandidates = new KeyValuePair<string, string>[] {
            new KeyValuePair<string,string>("WinRTActorSupport, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "System.Threading.Actors.WinRTSupport.WinRTSocketProvider"),
//@TODO     new KeyValuePair<string,string>("WinRTActorSupportWindowsStore, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "System.Threading.Actors.WinRTSupport.WinRTSocketProvider"),
            new KeyValuePair<string,string>("System.Threading.Actors.Services, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "System.Threading.Actors.Utilities.WrappedTcpClient")
        }.ToDictionary(kvp => kvp.Key,kvp=>kvp.Value);

        // The resolved type for creating ITcpClients
        private static Type s_tcpClientType;

        static LightupITcpClientFactory()
        {
            // In the static constructor, attempt to resolve the proper type for creating an ITcpClient
            // Go through the list of candidates one by one.
            foreach (var kvp in s_ClientProviderCandidates)
            {
                s_tcpClientType = Type.GetType(kvp.Value + ", " + kvp.Key, false);
                if (s_tcpClientType != null) break;
            }
        }

        // If no one bothers to place their own CreateTcpClientFunc, we will default to this routine 
        // for creating an ITcpClient.
        private static ITcpClient DefaultCreateTcpClient(string hostname, int port)
        {
            if(s_tcpClientType == null)
                throw new NotImplementedException("Failed to resolve ITcpClient creation type");

            Debug.WriteLine("DefaultCreateTcpClient called");
            ITcpClient result = null;
            if (hostname == null && port == -1)
            {
                // For testing
                // Change the port number to -2 to signify that we went through the reflection-based creation method
                result = (ITcpClient)Activator.CreateInstance(s_tcpClientType, hostname, -2);
            }
            else
            {
                result = (ITcpClient)Activator.CreateInstance(s_tcpClientType, hostname, port);
            }

            return result;
        }

        // By default, use DefaultCreateTcpClient to create a TCP client
        private static Func<string, int, ITcpClient> s_createTcpClientFunc = DefaultCreateTcpClient;

        // Downstream non-portable classes can replace the default s_createTcpClientFunc with one that suits their needs
        public static Func<string, int, ITcpClient> CreateTcpClientFunc
        {
            get { return s_createTcpClientFunc; }
            set 
            {
                Debug.WriteLine("ActorRawTcpClient: Replacing s_createTcpClientFunc");
                s_createTcpClientFunc = value; 
            }
        }
    }
}
